package com.madou.dbtool.redis.manager;

import com.madou.common.annotation.HideClass;
import com.madou.dbtool.language.redis.parser.RedisContentTypeParser;
import com.madou.dbtool.redis.utils.BytesToStringUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisMonitor;
import redis.clients.jedis.Protocol;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * @author miukoo
 * Redis 直连客户端
 */
@HideClass("directRedisSourceManager")
public class DirectRedisSourceManager implements RedisSourceManager {
    String host;
    int port;
    Jedis jedis;
    String password;
    // 标记是否为集群
    boolean cluster=false;
    // 集群连接器
    ClusterRedisSourceManager clusterRedisSourceManager;

    public DirectRedisSourceManager(String host,int port,String password){
        this.host = host;
        this.port = port;
        this.password = parsePassword(password);
    }

    private synchronized Jedis connection(){
        if(jedis==null||!jedis.isConnected()) {
            jedis = RedisSourceManagerFactory.getConnection(host,port,password);
        }
        return jedis;
    }

//    public static void main(String[] args) {
////        DirectRedisSourceManager ds = new DirectRedisSourceManager("192.168.56.102",6380);
//        DirectRedisSourceManager ds = new DirectRedisSourceManager("localhost",6379,null);
//        ds.connection();
//        ScanParams scanParams = new ScanParams();
//        scanParams.count(200);
//        scanParams.match("*1*");
//        Protocol.Command command = Protocol.Command.valueOf("TYPE".toUpperCase(Locale.ROOT));
//        String temp = "\\ufffd\\ufffd\\u0000\\u0005t\\u0000\\u0008test_abc";
//        temp = "rO0ABXQACHRlc3RfYWJj";
//        byte[] encode = Base64.decode(temp);
////        temp = BytesToStringUtil.string2byte(temp);
//        System.out.println(temp);
//        byte[] type = (byte[]) ds.jedis.sendCommand(command, encode);
//        String keyType = new String(type);
//        System.out.println(keyType);
////        ScanResult<String> scan = ds.jedis.scan("0", scanParams);
////        System.out.println(ds.getNodes());
////
////        for (int i = 2000; i < 10000; i++) {
////            System.out.print(" "+i);
////        }
//    }

    @Override
    public String getDataSourceName() {
        return host+"-"+port;
    }

    @Override
    public int getDBCount() {
        connection();
        return Integer.parseInt(jedis.configGet("databases").get("databases"));
    }

    @Override
    public boolean isCluster() {
        return cluster;
    }

    @Override
    public List<RedisNodeInfo> getNodes() {
        connection();
        String info = null;
        try {
            info = jedis.info();
        }catch (Exception e){
            e.printStackTrace();
            jedis = null;
            RedisSourceManagerFactory.clearConnection(host,port);
            connection();
            info = jedis.info();
        }
        List<RedisNodeInfo> list = new ArrayList<>();
        if(info.contains("cluster_enabled:1")){
            cluster=true;
            ClusterRedisSourceManager clusterRedisSourceManager = new ClusterRedisSourceManager(host,port,password);
            String[] nodes = jedis.clusterNodes().split("\n");
            for (String node : nodes) {
                //779b9e5798a60efd906da5edc45b562eb9929952 192.168.56.102:6382@16382 master - 0 1645750290688 2 connected 10000-16383
                String tmp[] = node.split(" ");
                String addr[] = tmp[1].split("@");
                RedisNodeInfo redisNodeInfo = new RedisNodeInfo();
                redisNodeInfo.setIp(addr[0].split(":")[0]);
                redisNodeInfo.setPort(Integer.valueOf(addr[0].split(":")[1]));
                redisNodeInfo.setRole(tmp[2]);
                if(redisNodeInfo.getIp().equals(host)&&redisNodeInfo.getPort()==port){
                    redisNodeInfo.setRedisSourceManager(new ClusterRedisSourceManager(host,port,password,this));
                }else {
                    redisNodeInfo.setRedisSourceManager(new ClusterRedisSourceManager(redisNodeInfo.getIp(),redisNodeInfo.getPort(),password));
                }
                list.add(redisNodeInfo);
            }
        }else {
            RedisNodeInfo redisNodeInfo = new RedisNodeInfo();
            redisNodeInfo.setIp(host);
            redisNodeInfo.setPort(port);
            redisNodeInfo.setRedisSourceManager(this);
            list.add(redisNodeInfo);
        }
        return list;
    }

    @Override
    public Long getDBSize(int index) {
        connection();
        jedis.select(index);
        return jedis.dbSize();
    }

    /**
     * 获取第一页的数据
     * @return
     */
    @Override
    public List<byte[]> getFirst(int index) {
        connection();
        jedis.select(index);
        return jedis.scan("0".getBytes()).getResult();
    }

    /**
     * 执行命令
     * @param commandText
     * @return
     */
    @Override
    public Object execute(int index,String commandText){
        connection();
        if(index>=0) {
            jedis.select(index);
        }
        String[] temp = commandText.trim().split(" ");
        Protocol.Command command = Protocol.Command.valueOf(temp[0].toUpperCase(Locale.ROOT));
        List<byte[]> args = new ArrayList<>();
        for (int i = 1; i < temp.length; i++) {
            if(temp[i]!=""&&temp[i]!=" "){
                args.add(BytesToStringUtil.string2Byte(temp[i]));
                if(i==1) {
                    RedisContentTypeParser.redisKeywordTrieLoader.addWord(temp[i]);
                }
            }
        }
        byte[][] commandArgs = new byte[args.size()][];
        args.toArray(commandArgs);
        if(commandText.length()<20){
            RedisContentTypeParser.redisKeywordTrieLoader.addWord(commandText);
        }
        return jedis.sendCommand(command, commandArgs);
    }

    /**
     * 执行一条命令
     * @param commandArgs
     * @return
     */
    @Override
    public Object execute(int index,Protocol.Command command, byte[][] commandArgs){
        connection();
        if(index>=0) {
            jedis.select(index);
        }
        return jedis.sendCommand(command, commandArgs);
    }


    /**
     * 开启监控
     * @param jedisMonitor
     * @return
     */
    @Override
    public void monitor(JedisMonitor jedisMonitor){
        connection();
        jedis.monitor(jedisMonitor);
    }

    @Override
    public RedisSourceManager cloneDirect() {
        return new DirectRedisSourceManager(host,port,password);
    }

    @Override
    public RedisSourceManager cloneCluster() {
        return cloneDirect();
    }

    @Override
    public void close() {
        if(jedis!=null){
            jedis.disconnect();
            jedis.close();
            jedis = null;
        }
    }

}
